package com.rephilo.mvc.framework.servlet;

import com.rephilo.mvc.framework.annotions.*;
import com.rephilo.mvc.framework.pojo.Handler;
import com.rephilo.mvc.framework.service.MySecurityManager;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author rephilo
 */
public class DispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 372067899573109121L;

    private Properties properties = new Properties();

    private List<String> classNames = new ArrayList<>();

    private Map<String, Object> ioc = new HashMap<>();

    /**
     * handlerMapping
     */
//    private Map<String, Method> handlerMapping = new HashMap<>();
    private List<Handler> handlerMapping = new ArrayList<>();

    /**
     * 接收处理请求
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    /**
     * 接收处理请求
     *
     * @param req
     * @param resp
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();

        Handler handler = getHandler(req);
        if (handler == null) {
            resp.getWriter().write("404 not found");
        }

        boolean authorized = false;
        if (handler != null) {
            //权限校验
            if (handler.getSecurityRoles() == null ||
                    handler.getSecurityRoles().isEmpty()) {
                //当不限制访问权限时，直接放过
                authorized = true;
            } else {
                //获取当前登陆的userName
                String username = req.getParameter("username");
                if (StringUtils.isNotBlank(username)) {
                    if (handler.getSecurityRoles().contains("*")) {
                        authorized = true;
                    } else {
                        MySecurityManager securityManager = (MySecurityManager) ioc.get("mySecurityManager");
                        String role = securityManager.getRoleByUserName(username);
                        if (handler.getSecurityRoles().contains(role)) {
                            authorized = true;
                        }
                    }
                }
            }

            if (authorized) {
                Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

                Object[] args = new Object[parameterTypes.length];

                Map<String, String[]> parameterMap = req.getParameterMap();

                for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                    String value = StringUtils.join(entry.getValue(), ",");

                    if (handler.getParamIndexMapping().containsKey(entry.getKey())) {
                        Integer index = handler.getParamIndexMapping().get(entry.getKey());

                        args[index] = value;
                    }
                }

                int requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
                args[requestIndex] = req;

                int responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
                args[responseIndex] = resp;

                try {
                    Object invoke = handler.getMethod().invoke(handler.getController(), args);
                    resp.setStatus(200);
                    if (invoke != null) {
                        resp.getWriter().write(invoke.toString());
                    }
                } catch (Exception e) {
                    resp.setStatus(500);
                    e.printStackTrace();
                }
            } else {
                //权限校验未通过时，返回401
                resp.setStatus(401);
                resp.getWriter().write("Unauthorized");
            }
        }
    }

    private Handler getHandler(HttpServletRequest req) {
        if (handlerMapping.isEmpty()) {
            return null;
        }

        String url = req.getRequestURI();
        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.getPattern().matcher(url);
            if (matcher.matches()) {
                return handler;
            }
        }

        return null;
    }

    /**
     * 初始化
     *
     * @param config
     * @throws ServletException
     */
    @Override
    public void init(ServletConfig config) throws ServletException {
        //加载配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        try {
            doLoadConfig(contextConfigLocation);
            //扫描相关注解
            doScan(properties.getProperty("scanPackage"));
            //初始化bean
            doInstance();
            //实现依赖注入
            doAutowired();
            //handlerMapping，建立映射关系
            initHandlerMapping();
        } catch (Exception e) {

        }

        System.out.println("初始化完成");
        //等待请求进入
    }

    /**
     * 加载配置文件
     *
     * @param contextConfigLocation
     */
    private void doLoadConfig(String contextConfigLocation) throws IOException {
        InputStream stream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        properties.load(stream);
    }

    /**
     * 扫描相关注解
     */
    private void doScan(String scanPackage) {
        String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");
        File pack = new File(scanPackagePath);
        File[] files = pack.listFiles();

        for (File file : files) {
            if (file.isDirectory()) {
                doScan(scanPackage + "." + file.getName());
            } else if (file.getName().endsWith(".class")) {
                String classname = scanPackage + "." + file.getName().replace(".class", "");
                classNames.add(classname);
            }
        }
    }

    /**
     * 初始化bean
     */
    private void doInstance() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        if (classNames.size() > 0) {
            for (String className : classNames) {
                Class<?> aClass = Class.forName(className);
                //区分Controller和Service
                if (aClass.isAnnotationPresent(Controller.class)) {
                    //Controller Id 首字母小写
                    String simpleName = lowerFirst(aClass.getSimpleName());
                    Object obj = aClass.newInstance();
                    ioc.put(simpleName, obj);
                } else if (aClass.isAnnotationPresent(Service.class)) {
                    Service annotation = aClass.getAnnotation(Service.class);
                    String beanName = annotation.value();
                    if ("".equals(beanName)) {
                        beanName = lowerFirst(aClass.getSimpleName());
                    }
                    ioc.put(beanName, aClass.newInstance());

                    //接口处理
                    Class<?>[] interfaces = aClass.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        //TODO:这里能new一个对象？
                        ioc.put(anInterface.getName(), aClass.newInstance());
                    }
                }
            }
        }
    }

    /**
     * 实现依赖注入
     */
    private void doAutowired() throws IllegalAccessException {
        if (!ioc.isEmpty()) {
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                Object obj = entry.getValue();
                Field[] fields = obj.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Autowired annotation = field.getAnnotation(Autowired.class);
                        String beanName = annotation.value();
                        if ("".equals(beanName)) {
                            beanName = field.getType().getName();
                        }

                        field.setAccessible(true);
                        field.set(obj, ioc.get(beanName));
                    }
                }
            }
        }
    }

    /**
     * handlerMapping，建立映射关系
     * 重点
     * 将url和method建立关联
     */
    private void initHandlerMapping() {
        if (!ioc.isEmpty()) {
            for (Map.Entry<String, Object> entry : ioc.entrySet()) {
                Class<?> aClass = entry.getValue().getClass();
                if (!aClass.isAnnotationPresent(Controller.class)) {
                    continue;
                }

                String baseUrl = "";
                if (aClass.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping classAnnotation = aClass.getAnnotation(RequestMapping.class);
                    baseUrl = classAnnotation.value();
                }

                for (Method method : aClass.getMethods()) {
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        String url = baseUrl + method.getAnnotation(RequestMapping.class).value();

                        Handler handler = new Handler(entry.getValue(), method, Pattern.compile(url));
                        Parameter[] parameters = method.getParameters();
                        for (int i = 0; i < parameters.length; i++) {
                            Parameter parameter = parameters[i];
                            if (parameter.getType() == HttpServletRequest.class ||
                                    parameter.getType() == HttpServletResponse.class) {
                                handler.getParamIndexMapping().put(parameter.getType().getSimpleName(), i);
                            } else {
                                handler.getParamIndexMapping().put(parameter.getName(), i);
                            }
                        }

                        //设置校验可以访问当前方法的角色
                        if (method.isAnnotationPresent(Security.class)) {
                            String roles = method.getAnnotation(Security.class).roles();
                            handler.setSecurityRoles(Arrays.asList(roles.split(",")));
                        }
                        handlerMapping.add(handler);
                    }
                }
            }
        }
    }

    /**
     * 首字母小写
     *
     * @return
     */
    public String lowerFirst(String str) {
        char[] chars = str.toCharArray();
        if (chars[0] >= 'A' && chars[0] <= 'Z') {
            chars[0] += 32;
        }

        return String.valueOf(chars);
    }
}
